package reader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import menu.Menu;
import menu.TextMenu;
import menu.TextMenuElement;


public class XMLMenuReader implements MenuReader {

    public XMLMenuReader() {

    }

    public Menu getMenu(File pMenuFile) {
        assert pMenuFile != null;

        // Obtain xml document to build menu
        // Catch potential exceptions
        Element lRoot = null;
        try {
            lRoot = getMenuRoot(pMenuFile);
        } catch (ParserConfigurationException e) {
            System.err.println(e.getLocalizedMessage());
            return null;
        } catch (SAXException e) {
            System.err.println(e.getLocalizedMessage());
            return null;
        } catch (IOException e) {
            System.err.println(e.getLocalizedMessage());
            return null;
        }

        // Initialize menu with prompt and menu title
        String lMenuPrompt = getSimpleElementText(lRoot, "prompt");
        String lMenuTitle = getSimpleElementText(lRoot, "title");
        Menu lMenu = new TextMenu(lMenuPrompt, lMenuTitle);

        // Populate menu with elements
        NodeList lElementList = lRoot.getElementsByTagName("element");
        for(int i = 0; i < lElementList.getLength(); i++) {
            Element lElement = (Element) lElementList.item(i);
            
            // Get menu element id, hotkey and description
            int lID = Integer.parseInt(lElement.getAttribute("id"));
            String lHotkey = getSimpleElementText(lElement, "hotkey");
            String lDescription = getSimpleElementText(lElement, "description");

            // See if this menu option links to another menu
            try {
                String lMenuLink = getSimpleElementText(lElement, "link");
                lMenu.addElement(new TextMenuElement(lID, lHotkey.charAt(0), lDescription, lMenuLink));
            } catch (RuntimeException e) {
                lMenu.addElement(new TextMenuElement(lID, lHotkey.charAt(0), lDescription));

            }

        }

        return lMenu;
    }

    private Element getMenuRoot(File pMenuFile) throws ParserConfigurationException, IOException, SAXException {
        assert pMenuFile != null;

        // Obtain xml document to build menu
        DocumentBuilderFactory lFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder lBuilder = lFactory.newDocumentBuilder();
        FileInputStream lStream = new FileInputStream(pMenuFile);
        Document lDocument = lBuilder.parse(lStream);
        lStream.close();

        return lDocument.getDocumentElement();
    }

    private Element getFirstElement(Element pElement, String pName) {
        assert pElement != null && pName != null;
        
        NodeList lList = pElement.getElementsByTagName(pName);
        // Check that list is populated
        if (lList.getLength() < 1) {
            throw new RuntimeException(
                    "Element: " + pElement + " does not contain: " + pName);
        }
        return (Element) lList.item(0);
    }

    private String getSimpleElementText(Element pNode, String pName) {
        assert pNode != null && pName != null;
        
        Element namedElement = getFirstElement(pNode, pName);
        return getSimpleElementText(namedElement);
    }

    private String getSimpleElementText(Element pNode) {
        assert pNode != null;
        
        StringBuffer lBuffer = new StringBuffer();
        NodeList lChildren = pNode.getChildNodes();
        
        // Get text in node
        for (int i = 0; i < lChildren.getLength(); i++) {
            Node lChild = lChildren.item(i);
            if (lChild instanceof Text) {
                lBuffer.append(lChild.getNodeValue());
            }
        }
        return lBuffer.toString();
    }
}
